Vuex 与 Pinia
涵盖 Vuex 与 Pinia 在 5 维度上的差异、Pinia 移除 mutations 的原因、模块化对比、Vue 3 项目选型与迁移方案。
一、标准面试回答(1 分钟)
Vuex 和 Pinia 都是 Vue 的状态管理库,Pinia 是 Vue 3 官方推荐的新方案,可以看作是 Vuex 5 的雏形,在设计上更简洁、类型推断更好、API 更现代化。
从五个维度对比:
第一,设计理念
Vuex 采用单一状态树,通过 mutations(同步修改)和 actions(异步操作)区分修改方式;Pinia 移除了 mutations,直接用 actions 处理同步和异步,更符合直觉,代码量也更少。
第二,TypeScript 支持
Pinia 天然为 TypeScript 设计,定义 store 时自动推断类型,不需要额外标注;Vuex 4 虽然支持 TS,但写法繁琐,需要定义大量类型模板。
第三,API 风格
Pinia 支持组合式 API 风格的 store 定义(类似 Vue 3 的 setup),也支持选项式 API,灵活性更高;Vuex 主要是选项式风格,通过 mapState、mapActions 等辅助函数在组件中使用。
第四,模块化
Vuex 通过 modules 实现模块化,但模块之间嵌套复杂,命名空间需要手动配置;Pinia 每个 store 天然就是独立模块,按需导入,不需要嵌套,代码分割更友好。
第五,体积和性能
Pinia 更轻量,体积比 Vuex 小很多,且利用了 Vue 3 的响应式系统,性能更好。Vuex 4 为了兼容 Vue 2 保留了部分旧机制,体积和性能都不如 Pinia。
迁移建议
- 新项目直接用 Pinia
- Vue 2 项目如果要用 Pinia 需要配合
@vue/composition-api插件,但官方更建议 Vue 2 继续用 Vuex 3 - Vue 3 项目从 Vuex 迁移到 Pinia 成本较低,API 改动不大
二、20 秒极简版
Pinia 是 Vue 3 官方推荐的状态管理库,相比 Vuex 更轻量、TS 支持更好、API 更简洁。核心区别:Pinia 没有 mutations,直接 actions 处理同步异步;每个 store 独立模块;天然组合式 API 风格。新项目用 Pinia,Vue 2 项目继续用 Vuex 3。
三、五维度对比表
| 维度 | Vuex | Pinia |
|---|---|---|
| 设计理念 | mutations + actions | 移除 mutations,actions 处理同步/异步 |
| TypeScript | 支持但繁琐 | 天然友好,类型自动推断 |
| API 风格 | 选项式 + mapState/mapActions | 组合式 + 选项式 |
| 模块化 | modules 嵌套,需手动命名空间 | 每个 store 独立模块 |
| 体积性能 | 较大,保留 Vue 2 兼容机制 | 更轻量,性能更好 |
四、追问 1:Pinia 为什么移除了 mutations?
官方移除 mutations 主要基于三点考虑:
第一,mutations 的本质是约束,但实际开发中很多人直接在 actions 里修改状态,mutations 变成了一层"形式主义"的封装,反而增加了样板代码。
第二,Vue 3 的响应式系统更强大,Pinia 的 state 本身就是响应式对象,通过 store.count++ 直接修改也能被追踪。Vuex 的 mutations 设计主要是为了配合 Vue 2 的响应式限制和 DevTools 追踪,Vue 3 下这些限制不再成立。
第三,TypeScript 推断更简单,没有 mutations 层后,类型定义大幅简化。
会不会有问题? 实际上不会。Pinia 仍然通过 $patch 提供批量更新,DevTools 对直接修改的追踪能力也很完善。如果团队需要强制"不允许直接修改状态",可以通过约定或 lint 规则控制,而不是框架强制。
五、追问 2:Pinia 中怎么实现模块化?
Pinia 的模块化比 Vuex 简单得多:每个 store 就是独立的模块,不需要嵌套配置。
// stores/user.js
export const useUserStore = defineStore('user', {
state: () => ({ name: '', token: '' }),
actions: { logout() { this.token = '' } }
})
// stores/cart.js
export const useCartStore = defineStore('cart', {
state: () => ({ items: [] }),
actions: {
checkout() {
const userStore = useUserStore() // 直接引入其他 store
if (!userStore.token) throw new Error('未登录')
// 结算逻辑
}
}
})关键点:
- 模块之间互相引用时,直接在 action 或 getter 里调用
useXxxStore()即可 - 注意避免循环依赖,如果出现可以拆分为更细粒度的 store
- Pinia 会自动处理跨 store 的响应性,一个 store 的变化可以触发另一个 store 的 computed 重新计算
六、追问 3:Vuex 4 和 Pinia 在 Vue 3 项目中怎么选?
建议:新项目无脑上 Pinia,老项目迁移看情况。
选 Pinia 的理由
- 代码量减少 30-50%,开发效率高
- TypeScript 体验碾压 Vuex 4,几乎不需要写类型声明
- 体积更小,性能更好
- 官方钦定的下一代方案,社区生态已经全面转向
- 学习成本低,不需要理解 mutations 和 actions 的区别
继续用 Vuex 4 的场景
- 老项目已经大量使用 Vuex,迁移成本高,且短期内不会重构
- 团队对 Vuex 非常熟悉,暂时不想引入新概念
- 使用了 Vuex 特有的插件生态(部分插件可能还没迁移到 Pinia)
迁移要点
如果是从 Vuex 迁移到 Pinia,官方提供了迁移指南,API 层面的改动主要是:
state写法微调mutations合并到actions- 模块拆分为独立 store
mapState等辅助函数替换为storeToRefs
七、易错点
- 说"Pinia 是 Vuex 5":严格来说 Pinia 不是 Vuex 5,官方曾有过将 Pinia 作为 Vuex 5 候选方案,但最终决定独立发展。能说出"Pinia 可以理解为 Vuex 5 的设计理念继承者"会更准确
- 认为 Pinia 只支持 Vue 3:Pinia 也支持 Vue 2(需配合
@vue/composition-api),只是 Vue 2 场景下更推荐 Vuex 3 - 混淆 mutations 和 actions 的本质区别:说"mutations 是同步,actions 是异步"只对了一半。能补充"mutations 用于追踪状态变更,actions 可以包含任意异步操作,但最终还是要 commit mutations"才完整
- Pinia 的 TypeScript 优势说不清楚:只说"TS 支持好",但说不出"定义 store 时自动推断 state、getters、actions 类型,不需要额外泛型"这种细节
- 模块化对比只说"Pinia 不用 modules":要说明 Pinia 通过"每个 store 独立文件 + 互相导入"实现模块化
- 忽略
storeToRefs:Pinia 中解构 store 会丢失响应性,需要用storeToRefs包装 - 迁移建议过于绝对:说"所有项目都应该用 Pinia"不考虑 Vue 2 场景和迁移成本
八、关联文档
- 04-Vue 组件通信(props/$emit 之外的状态管理)